﻿using System.Collections.Generic;
using Cms.Application.Admin.Presenters;
using Cms.Application.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Cms.Domain;
using Cms.Application.Admin.Views;
using Moq;

namespace Cms.Tests.Unit.Application.Admin.Presenters
{
    [TestClass]
    public class EditFolderPresenterTests : BasePresenterTests
    {
        private Mock<IEditFolderView> _viewMock;
        private Site _site;
        
        private void Prepare()
        {
            InitRepositoryFactoryMock();
            _site = new Site { Domain = "www.fredium.com", RootMediaFolder = "media" };
            Administrator admin = new Administrator {CurrentSite = _site};
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator(1)).Returns(admin);
            IList<Site> sites = new List<Site> { _site };
            SiteRepositoryMock.Setup(s => s.GetSites()).Returns(sites);
            _viewMock = new Mock<IEditFolderView>();
            _viewMock.SetupAllProperties();
            _viewMock.Setup(v => v.AdministratorID).Returns(1);
            _viewMock.Setup(v => v.Localization).Returns("en-US");
            _viewMock.Setup(v => v.ContextTranslationFile).Returns("Admin/Translations/EditFolder.xml");
        }
        
        [TestMethod]
        public void Initialize_NewFolder_Success()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(-1);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(-1, _viewMock.Object.SelectedParentFolderID);
        }
        
        [TestMethod]
        public void Initialize_ExistingRootFolder_Success()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(1);
            Folder folder = new Folder { ID = 1, Name = "TheB", Site = _site };
            FolderRepositoryMock.Setup(f => f.GetFolder(1)).Returns(folder);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(-1, _viewMock.Object.SelectedParentFolderID);
            Assert.AreEqual("TheB", _viewMock.Object.Name);
        }

        [TestMethod]
        public void Initialize_ExistingSubFolder_Success()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(1);
            Folder parent = new Folder { ID = 10, Name = "TheParent", Site = _site };
            Folder folder = new Folder { ID = 1, Name = "TheB", Site = _site, Parent = parent };
            parent.SubFolders.Add(folder);
            FolderRepositoryMock.Setup(f => f.GetFolder(1)).Returns(folder);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(10, _viewMock.Object.SelectedParentFolderID);
        }
        
        [TestMethod]
        public void Save_NoFolderName_Error()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(-1);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("");
            presenter.Save();
            Assert.IsFalse(presenter.IsValid);
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(1)); // 1 = NoNameError
        }
        
        private void TestInvalidFolderName(EditFolderPresenter presenter, string name)
        {
            presenter.ClearErrors();
            _viewMock.Setup(v => v.Name).Returns(name);
            presenter.Save();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(2)); // 2 = ReservedNameError
        }
        
        [TestMethod]
        public void Save_InvalidFolderName_Error()
        {
            Prepare();
            _viewMock.Setup(v => v.FolderID).Returns(-1);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            TestInvalidFolderName(presenter, "bin");
            TestInvalidFolderName(presenter, "__system");
            TestInvalidFolderName(presenter, "admin");
            TestInvalidFolderName(presenter, "swfupload");
            TestInvalidFolderName(presenter, "media"); // The site´s RootMediaFolder
            // Test names reserved in web.config  (or app.config in this case...)
            TestInvalidFolderName(presenter, "a");
            TestInvalidFolderName(presenter, "b");
            TestInvalidFolderName(presenter, "c");
        }
        
        [TestMethod]
        public void Save_SelfAsParentFolder_Error()
        {
            Prepare();
            Folder folder = new Folder {ID = 22, Name = "Beer", Site = _site};
            FolderRepositoryMock.Setup(f => f.GetFolder(22)).Returns(folder);
            _viewMock.Setup(v => v.FolderID).Returns(22);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("Beer"); //This is needed to get passed to the line I want to test
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(22);
            presenter.Save();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(3)); // 3 = SaveInSelfError
        }
        
        [TestMethod]
        public void Save_SubfolderAsParent_Error()
        {
            Prepare();
            // Setup a folder structure like this: domain/A/B/C
            Folder folderA = new Folder {ID = 1, Name = "A", Site = _site};
            Folder folderB = new Folder {ID = 2, Name = "B", Site = _site};
            Folder folderC = new Folder {ID = 3, Name = "C", Site = _site};
            folderB.Parent = folderA;
            folderC.Parent = folderB;
            FolderRepositoryMock.Setup(f => f.GetFolder(1)).Returns(folderA);
            FolderRepositoryMock.Setup(f => f.GetFolder(3)).Returns(folderC);
            _viewMock.Setup(v => v.FolderID).Returns(1); // Edit folder A
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("A");
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(3); // Try to save with folderC as parent
            presenter.Save();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(4)); // 4 = SaveInChildError
        }
        
        [TestMethod]
        public void Save_NamingConflict_Error()
        {
            Prepare();
            Folder folderA = new Folder { Site = _site, ID = 1, Name = "folderA" };
            _site.Folders.Add(folderA);
            Folder folderB = new Folder { Site = _site, ID = 2, Name = "folderB" };
            _site.Folders.Add(folderB);
            // Try to rename folderB to folderA.
            FolderRepositoryMock.Setup(f => f.GetFolder(2)).Returns(folderB);
            _viewMock.Setup(v => v.FolderID).Returns(2); // Test on folder B
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Setup(v => v.Name).Returns("folderA");
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(-1); // Try to save in root
            presenter.Save();
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(5)); // 5 = NameCollisionError
        }
        
        [TestMethod]
        public void CollectOriginalPathsForUpdate()
        {
            Prepare();
            Folder folderA = new Folder { Site = _site, ID = 1, Name = "folderA" };
            _site.Folders.Add(folderA);
            Folder folderB = new Folder { Site = _site, ID = 2, Name = "folderB", Parent = folderA };
            _site.Folders.Add(folderB);
            folderA.SubFolders.Add(folderB);
            Page page1 = new Page { ID = 1, Filename = "page1.aspx", Folder = folderA };
            Page page2 = new Page { ID = 2, Filename = "page2.aspx", Folder = folderB };
            folderA.Pages.Add(page1);
            folderB.Pages.Add(page2);
            _viewMock.Setup(v => v.FolderID).Returns(1); 
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            IDictionary<int, string> originalPaths = new Dictionary<int, string>();
            presenter.CollectOriginalPathsForUpdate(folderA, ref originalPaths);
            Assert.AreEqual(2, originalPaths.Count);
            Assert.IsTrue(originalPaths.Contains(new KeyValuePair<int, string>(1, "folderA/page1.aspx")));
            Assert.IsTrue(originalPaths.Contains(new KeyValuePair<int, string>(2, "folderA/folderB/page2.aspx")));
        }
        
        [TestMethod]
        public void Save_SetName_Success()
        {
            Prepare();
            Folder folderA = new Folder { Site = _site, ID = 1, Name = "folderA" };
            _site.Folders.Add(folderA);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("updatedName");
            FolderRepositoryMock.Setup(f => f.GetFolder(1)).Returns(folderA);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.DiskAccess = new Mock<IDiskAccess>().Object; // The Save method requires an IO object
            presenter.Save();
            Assert.AreEqual("updatedName", folderA.Name);
        }

        [TestMethod]
        public void Save_SetParentFolder_Success()
        {
            Prepare();
            Folder folderA = new Folder { Site = _site, ID = 1, Name = "folderA" };
            Folder folderB = new Folder { Site = _site, ID = 2, Name = "folderB" };
            _site.Folders.Add(folderA);
            _site.Folders.Add(folderB);
            FolderRepositoryMock.Setup(f => f.GetFolder(1)).Returns(folderA);
            FolderRepositoryMock.Setup(f => f.GetFolder(2)).Returns(folderB);
            _viewMock.Setup(v => v.FolderID).Returns(1);
            _viewMock.Setup(v => v.Name).Returns("folderA"); // Needed to pass validation
            _viewMock.Setup(v => v.SelectedParentFolderID).Returns(2);
            EditFolderPresenter presenter = new EditFolderPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.DiskAccess = new Mock<IDiskAccess>().Object; // The Save method requires an IO object
            presenter.Save();
            Assert.AreSame(folderB, folderA.Parent);
        }
    }
}
